﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities.Rules;
using System.IO;
using System.Xml;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Activities.Rules.Design;
using System.Windows.Forms;

namespace RelationExpertSystem
{
    public class RelationRulesManager : IRulesManager
    {
        private static object lockObject = new object();

        private RuleSet _rules;
        private string _filename; // = "RelationshipRules.ruleset";

        public void NewRules(string name, string description)
        {
            _rules = new RuleSet(name, description);
            _filename = null;
        }

        public void LoadRules()
        {
            do
            {
                OpenFileDialog fd = new OpenFileDialog();
                fd.DefaultExt = "ruleset";
                fd.AddExtension = true;
                fd.Filter = "RuleSet file (*.ruleset)|*.ruleset";
                if (fd.ShowDialog() != DialogResult.OK)
                    return;
                _filename = fd.FileName;
            } while (_filename == null || !File.Exists(_filename));

            XmlTextReader xmlReader = new XmlTextReader(_filename);
            WorkflowMarkupSerializer wfmSerializer = new WorkflowMarkupSerializer();
            object results = wfmSerializer.Deserialize(xmlReader);
            xmlReader.Close();
            RuleSet ruleset = results as RuleSet;

            if (ruleset != null)
                _rules = ruleset;
        }

        public void EditRules()
        {
            if (_rules == null)
                LoadRules();

            RuleSetDialog dialog = new RuleSetDialog(typeof(PersonPair), null, _rules);

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                _rules = dialog.RuleSet;
                SaveRules();
            }
        }

        public void SaveRules()
        {
            while (_filename == null)
            {
                SaveFileDialog fd = new SaveFileDialog();
                fd.DefaultExt = "ruleset";
                fd.AddExtension = true;
                fd.Filter = "RuleSet file (*.ruleset)|*.ruleset";
                if (fd.ShowDialog() != DialogResult.OK)
                    return;

                _filename = fd.FileName;
            }

            XmlTextWriter xmlWriter = new XmlTextWriter(_filename, null);
            WorkflowMarkupSerializer wfmSerializer = new WorkflowMarkupSerializer();
            wfmSerializer.Serialize(xmlWriter, _rules);
            xmlWriter.Close();
        }

        public IEnumerable<Rule> GetRules()
        {
            if (_rules == null)
                return new List<Rule>();

            return _rules.Rules;
        }

        public void FireRules<T>(IEnumerable<IRuleTarget<T>> targets)
        {
            if (_rules == null)
                LoadRules();

            foreach (IRuleTarget<T> target in targets)
            {
                //target.Inference += new InferenceCallback<T>(HandleInference);
                target.Inference = new InferenceCallback<T>(HandleInference);
            }

            // how do I define a rule that accounts for two or more Person objects? (knowledge)
            // how do I add multiple people (facts) to the inference engine?
            // how do I tell the inference engine to make all inferences with known facts? (forward chaining)
            // how do I query the inference engine for a specific information (inference)? (backward chaining)
            // how do I query the inference engine for the truth value of a specified relationship? (backward chaining)

            foreach (IRuleTarget<T> target in targets)
            {
                RuleValidation validation = new RuleValidation(target.GetType(), null);
                RuleExecution engine = new RuleExecution(validation, target);
                _rules.Execute(engine);
            }

            foreach (IRuleTarget<T> target in targets)
            {
                //target.Inference -= new InferenceCallback<T>(HandleInference);
                target.Inference = new InferenceCallback<T>(target.InferenceCallbackNoop<T>);
            }
        }

        // tighly couples; move this to a child class
        protected virtual void HandleInference<T>(T stateObject)
        {
            RelationEventArgs args = stateObject as RelationEventArgs;
            if (args != null)
                RaiseNewRelationInferred(args);
        }

        public event EventHandler<RelationEventArgs> NewRelationInferred;
        private void RaiseNewRelationInferred(RelationEventArgs args)
        {
            EventHandler<RelationEventArgs> eh = NewRelationInferred;
            if (eh != null)
                eh(this, args);
        }
    }
}
